home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / ip / manage / snmp / cmu-snmp1.0 / snmplib / snmp_agent.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-09-19  |  18.0 KB  |  624 lines

  1. /*
  2.  * Simple Network Management Protocol (RFC 1067).
  3.  *
  4.  */
  5. /***********************************************************
  6.     Copyright 1988, 1989 by Carnegie Mellon University
  7.  
  8.                       All Rights Reserved
  9.  
  10. Permission to use, copy, modify, and distribute this software and its 
  11. documentation for any purpose and without fee is hereby granted, 
  12. provided that the above copyright notice appear in all copies and that
  13. both that copyright notice and this permission notice appear in 
  14. supporting documentation, and that the name of CMU not be
  15. used in advertising or publicity pertaining to distribution of the
  16. software without specific, written prior permission.  
  17.  
  18. CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  19. ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
  20. CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
  21. ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  22. WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  23. ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  24. SOFTWARE.
  25. ******************************************************************/
  26.  
  27. #ifdef KINETICS
  28. #include "gw.h"
  29. #include "ab.h"
  30. #include "inet.h"
  31. #include "fp4/cmdmacro.h"
  32. #include "fp4/pbuf.h"
  33. #include "glob.h"
  34. #endif
  35.  
  36. #if (defined(unix) && !defined(KINETICS))
  37. #include <sys/types.h>
  38. #include <netinet/in.h>
  39. #ifndef NULL
  40. #define NULL 0
  41. #endif
  42. #endif
  43.  
  44. #include "snmp.h"
  45. #include "snmp_impl.h"
  46. #include "asn1.h"
  47.  
  48. #include "mib.h"
  49. #include "snmp_vars.h"
  50.  
  51. void    snmp_input();
  52. void    snmp_trap();
  53. int    create_identical();
  54. int    parse_var_op_list();
  55. int    snmp_access();
  56.  
  57. char    version_descr[];
  58. oid    version_id[];
  59. int    version_id_len;
  60.  
  61. struct pbuf *definitelyGetBuf();
  62.  
  63. #define NUM_COMMUNITIES    5
  64. char    *communities[NUM_COMMUNITIES] = {
  65.     "public", 
  66.     "proxy",
  67.     "private",
  68.     "regional",
  69.     "core"
  70. };
  71.  
  72. /* these can't be global in a multi-process router */
  73.     u_char    sid[SID_MAX_LEN + 1];
  74.     int        sidlen;
  75.     u_char    *packet_end;
  76.     int        community;
  77.  
  78.  
  79. #ifdef KINETICS
  80. void
  81. snmp_input(p)
  82.     struct pbuf *p;
  83. {
  84.     struct ip        *ip = (struct ip *)p->p_off;
  85.     int            hlen = (int)ip->ip_hl << 2;
  86.     register struct udp        *udp;
  87.     register u_char *data;  /* pointer to the rest of the unread data */
  88.     int            length; /* bytes of data left in msg after the "data" pointer */
  89.     struct pbuf        *out_packet;
  90.     register u_char *out_data;
  91.     int            out_length;
  92.     u_short        udp_src;
  93.     extern struct mib_udp   mib_udp;
  94.  
  95.     
  96.     udp = (struct udp *)(p->p_off + hlen);
  97.     if (ntohs(ip->ip_len) - hlen < sizeof(struct udp) ||    /* IP length < minimum UDP packet */
  98.         ntohs(udp->length) > ntohs(ip->ip_len) - hlen){ /* UDP length > IP data */
  99.     ERROR("dropped packet with bad length");    /* delete me */
  100.     return; /* drop packet */
  101.     }
  102.     data = (u_char *)udp + sizeof(struct udp);
  103.     length = ntohs(udp->length) - sizeof(struct udp);
  104.  
  105.     out_packet = definitelyGetBuf(); /* drop packets off input queue if necessary */
  106.     out_data = (u_char *)(out_packet->p_off + sizeof (struct ip) + sizeof (struct udp));
  107.     out_length = MAXDATA - sizeof(struct ip) - sizeof (struct udp);
  108.  
  109. K_LEDON();
  110.     if (!snmp_parse(data, length, out_data, out_length, (u_long)ip->ip_src)){
  111.     K_PFREE(out_packet);
  112. K_LEDOFF();
  113.     return;
  114.     }
  115. K_LEDOFF();
  116.     out_packet->p_len = packet_end - (u_char *)out_packet->p_off;
  117.     setiphdr(out_packet, ip->ip_src);    /* address to source of request packet (ntohl ??? ) */
  118.     udp_src = ntohs(udp->src);
  119.     udp = (struct udp *)(out_packet->p_off + sizeof (struct ip));
  120.     udp->src = htons(SNMP_PORT);
  121.     udp->dst = htons(udp_src);
  122.     udp->length = out_packet->p_len - sizeof(struct ip);
  123.     udp->checksum = 0;    /* this should be computed */
  124.  
  125.     mib_udp.udpOutDatagrams++;
  126.     routeip(out_packet, 0, 0);
  127. }
  128.  
  129.  
  130. void
  131. snmp_trap(destAddr, trapType, specificType)
  132.     u_long  destAddr;
  133.     int        trapType;
  134.     int        specificType;
  135. {
  136.     struct pbuf        *out_packet;
  137.     register u_char *out_data;
  138.     register struct udp        *udp;
  139.     int            out_length;
  140.     static oid        sysDescrOid[] = {1, 3, 6, 1, 2, 1, 1, 1, 0};
  141.     
  142.     out_packet = definitelyGetBuf(); /* drop packets off input queue if necessary */
  143.     out_data = (u_char *)(out_packet->p_off + sizeof (struct ip) + sizeof (struct udp));
  144.     out_length = MAXDATA - sizeof(struct ip) - sizeof (struct udp);
  145.  
  146. K_LEDON();
  147.     out_packet->p_len = snmp_build_trap(out_data, out_length, version_id, version_id_len,
  148.     conf.ipaddr, trapType, specificType, TICKS2MS(tickclock)/10, sysDescrOid, sizeof(sysDescrOid)/sizeof(oid),
  149.     ASN_OCTET_STR, strlen(version_descr), (u_char *)version_descr);
  150.     if (out_packet->p_len == 0){
  151.     K_PFREE(out_packet);
  152. K_LEDOFF();
  153.     return;
  154.     }
  155. K_LEDOFF();
  156.     out_packet->p_len += sizeof(struct ip) + sizeof(struct udp);
  157.     setiphdr(out_packet, destAddr);    /* address to source of request packet (ntohl ??? ) */
  158.     udp = (struct udp *)(out_packet->p_off + sizeof (struct ip));
  159.     udp->src = htons(SNMP_PORT);
  160.     udp->dst = htons(SNMP_TRAP_PORT);
  161.     udp->length = out_packet->p_len - sizeof(struct ip);
  162.     udp->checksum = 0;    /* this should be computed */
  163.  
  164.     mib_udp.udpOutDatagrams++;
  165.     routeip(out_packet, 0, 0);
  166. }
  167. #endif
  168.  
  169. int
  170. snmp_parse(data, length, out_data, out_length, sourceip)
  171.     register u_char    *data;
  172.     int            length;
  173.     register u_char    *out_data;
  174.     int            out_length;
  175.     u_long        sourceip;    /* possibly for authentication */
  176. {
  177.     u_char        msg_type, type;
  178.     long        zero = 0;
  179.     long        reqid, errstat, errindex;
  180.     register u_char *out_auth, *out_header, *out_reqid;
  181.     u_char        *startData = data;
  182.     int            startLength = length;
  183.     long        version;
  184.     int            header_shift, auth_shift;
  185.  
  186.     sidlen = SID_MAX_LEN;
  187.     data = snmp_auth_parse(data, &length, sid, &sidlen, &version); /* authenticates message and returns length if valid */
  188.     if (data == NULL){
  189.     ERROR("bad authentication");
  190.     /* send auth fail trap */
  191.     return 0;
  192.     }
  193.     if (version != SNMP_VERSION_1){
  194.     ERROR("wrong version");
  195.     return NULL;
  196.     }
  197.     community = get_community(sid);
  198.     if (community == -1)
  199.     return NULL;
  200.     data = asn_parse_header(data, &length, &msg_type);
  201.     if (data == NULL){
  202.     ERROR("bad header");
  203.     return 0;
  204.     }
  205.     if (msg_type != GET_REQ_MSG && msg_type != GETNEXT_REQ_MSG && msg_type != SET_REQ_MSG){
  206.     return 0;
  207.     }
  208.     data = asn_parse_int(data, &length, &type, (u_long *)&reqid, sizeof(reqid));
  209.     if (data == NULL){
  210.     ERROR("bad parse of reqid");
  211.     return 0;
  212.     }
  213.     data = asn_parse_int(data, &length, &type, (u_long *)&errstat, sizeof(errstat));
  214.     if (data == NULL){
  215.     ERROR("bad parse of errstat");
  216.     return 0;
  217.     }
  218.     data = asn_parse_int(data, &length, &type, (u_long *)&errindex, sizeof(errindex));
  219.     if (data == NULL){
  220.     ERROR("bad parse of errindex");
  221.     return 0;
  222.     }
  223.     /*
  224.      * Now start cobbling together what is known about the output packet.
  225.      * The final lengths are not known now, so they will have to be recomputed
  226.      * later.
  227.      */
  228.     out_auth = out_data;
  229.     out_header = snmp_auth_build(out_auth, &out_length, sid, &sidlen, &zero, 0);
  230.     if (out_header == NULL){
  231.     ERROR("snmp_auth_build failed");
  232.     return 0;
  233.     }
  234.     out_reqid = asn_build_header(out_header, &out_length, (u_char)GET_RSP_MSG, 0);
  235.     if (out_reqid == NULL){
  236.     ERROR("");
  237.     return 0;
  238.     }
  239.  
  240.     type = (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER);
  241.     /* return identical request id */
  242.     out_data = asn_build_int(out_reqid, &out_length, type, (u_char *)&reqid, sizeof(reqid));
  243.     if (out_data == NULL){
  244.     ERROR("build reqid failed");
  245.     return 0;
  246.     }
  247.  
  248.     /* assume that error status will be zero */
  249.     out_data = asn_build_int(out_data, &out_length, type, (u_char *)&zero, sizeof(zero));
  250.     if (out_data == NULL){
  251.     ERROR("build errstat failed");
  252.     return 0;
  253.     }
  254.  
  255.     /* assume that error index will be zero */
  256.     out_data = asn_build_int(out_data, &out_length, type, (u_char *)&zero, sizeof(zero));
  257.     if (out_data == NULL){
  258.     ERROR("build errindex failed");
  259.     return 0;
  260.     }
  261.  
  262.     errstat = parse_var_op_list(data, length, out_data, out_length, msg_type, &errindex, 0);
  263.     if (msg_type == SET_REQ_MSG && errstat == SNMP_ERR_NOERROR){
  264.     /*
  265.      * SETS require 2 passes through the var_op_list.  The first pass verifies that
  266.      * all types, lengths, and values are valid, and the second does the set.  Then
  267.      * the identical GET RESPONSE packet is returned.
  268.      */
  269.     errstat = parse_var_op_list(data, length, out_data, out_length, msg_type, &errindex, 1);
  270.     if (create_identical(startData, out_auth, startLength, 0L, 0L))
  271.         return 1;
  272.     return 0;
  273.     }
  274.     switch((short)errstat){
  275.     case SNMP_ERR_NOERROR:
  276.         /*
  277.          * Because of the assumption above that header lengths would be encoded
  278.          * in one byte, things need to be fixed, now that the actual lengths are known.
  279.          */
  280.         header_shift = 0;
  281.         out_length = packet_end - out_reqid;
  282.         if (out_length >= 0x80){
  283.         header_shift++;
  284.         if (out_length > 0xFF)
  285.             header_shift++;
  286.         }
  287.         auth_shift = 0;
  288.         out_length = (packet_end - out_auth) - 2 + header_shift;
  289.         if (out_length >= 0x80){
  290.         auth_shift++;
  291.         if (out_length > 0xFF)
  292.             auth_shift++;
  293.         }
  294.         if (auth_shift + header_shift){
  295.         /*
  296.          * Shift packet (from request id to end of packet) by the sum of the
  297.          * necessary shift counts.
  298.          */
  299.         shift_array(out_reqid, packet_end - out_reqid, auth_shift + header_shift);
  300.         /* Now adjust pointers into the packet */
  301.         packet_end += auth_shift + header_shift;
  302.         out_reqid += auth_shift + header_shift;
  303.         out_header += auth_shift;
  304.         }
  305.         
  306.         /* re-encode the headers with the real lengths */
  307.         out_data = out_header;
  308.         out_length = packet_end - out_reqid;
  309.         out_data = asn_build_header(out_data, &out_length, GET_RSP_MSG, out_length);
  310.         if (out_data != out_reqid){
  311.         ERROR("internal error: header");
  312.         return 0;
  313.         }
  314.  
  315.         out_data = out_auth;
  316.         out_length = packet_end - out_auth;
  317.         out_data = snmp_auth_build(out_data, &out_length, sid, &sidlen, &zero, packet_end - out_header);
  318.         if (out_data != out_header){
  319.         ERROR("internal error");
  320.         return 0;
  321.         }
  322.         break;
  323.     case SNMP_ERR_NOSUCHNAME:
  324.     case SNMP_ERR_TOOBIG:
  325.     case SNMP_ERR_BADVALUE:
  326.     case SNMP_ERR_READONLY:
  327.     case SNMP_ERR_GENERR:
  328.         if (create_identical(startData, out_auth, startLength, errstat, errindex))
  329.         break;
  330.         return 0;
  331.     default:
  332.         return 0;
  333.     }
  334.     return 1;
  335. }
  336.  
  337. /*
  338.  * Parse_var_op_list goes through the list of variables and retrieves each one,
  339.  * placing it's value in the output packet.  If doSet is non-zero, the variable is set
  340.  * with the value in the packet.  If any error occurs, an error code is returned.
  341.  */
  342. int
  343. parse_var_op_list(data, length, out_data, out_length, msgtype, index, doSet)
  344.     register u_char    *data;
  345.     int            length;
  346.     register u_char    *out_data;
  347.     int            out_length;
  348.     u_char        msgtype;
  349.     register long    *index;
  350.     int            doSet;
  351. {
  352.     u_char  type;
  353.     oid        var_name[MAX_NAME_LEN];
  354.     int        var_name_len, var_val_len;
  355.     u_char  var_val_type, *var_val, statType;
  356.     register u_char *statP;
  357.     int        statLen;
  358.     u_short acl;
  359.     int        rw, exact;
  360.     int        access_method;
  361.     u_char  *headerP, *var_list_start;
  362.     int        dummyLen;
  363.     int        header_shift;
  364.  
  365.     if (msgtype == SET_REQ_MSG)
  366.     rw = WRITE;
  367.     else
  368.     rw = READ;
  369.     if (msgtype == GETNEXT_REQ_MSG)
  370.     exact = FALSE;
  371.     else
  372.     exact = TRUE;
  373.     data = asn_parse_header(data, &length, &type);
  374.     if (data == NULL){
  375.     ERROR("not enough space for varlist");
  376.     return PARSE_ERROR;
  377.     }
  378.     if (type != (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR)){
  379.     ERROR("wrong type");
  380.     return PARSE_ERROR;
  381.     }
  382.     headerP = out_data;
  383.     out_data = asn_build_header(out_data, &out_length, (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), 0);
  384.     if (out_data == NULL){
  385.         ERROR("not enough space in output packet");
  386.     return BUILD_ERROR;
  387.     }
  388.     var_list_start = out_data;
  389.  
  390.     *index = 1;
  391.     while((int)length > 0){
  392.     /* parse the name, value pair */
  393.     var_name_len = MAX_NAME_LEN;
  394.     data = snmp_parse_var_op(data, var_name, &var_name_len, &var_val_type, &var_val_len, &var_val, (int *)&length);
  395.     if (data == NULL)
  396.         return PARSE_ERROR;
  397.     /* now attempt to retrieve the variable on the local entity */
  398.     statP = getStatPtr(var_name, &var_name_len, &statType, &statLen, &acl, exact, &access_method);
  399.     if (statP == NULL)
  400.         return SNMP_ERR_NOSUCHNAME;
  401.     /* Check if this user has access rights to this variable */
  402.     if (!snmp_access(acl, community, rw)){
  403.         ERROR("authentication failed");
  404.         return SNMP_ERR_NOSUCHNAME;    /* bogus */
  405.         /* send_trap(); */
  406.     }
  407.     if (msgtype == SET_REQ_MSG){
  408.         /* see if the type and value is consistent with this entities variable */
  409.         if (!goodValue(var_val_type, var_val_len, statType, statLen)){
  410.         return SNMP_ERR_BADVALUE;
  411.         }
  412.         /* actually do the set if necessary */
  413.         if (doSet)
  414.         setVariable(var_val, var_val_type, var_val_len, statP, statLen);
  415.     }
  416.     /* retrieve the value of the variable and place it into the outgoing packet */
  417.     out_data = snmp_build_var_op(out_data, var_name, &var_name_len, statType, statLen, statP, &out_length);
  418.     if (out_data == NULL){
  419.         return SNMP_ERR_TOOBIG;
  420.     }
  421.  
  422.     (*index)++;
  423.     }
  424.     packet_end = out_data;  /* save a pointer to the end of the packet */
  425.  
  426.     /*
  427.      * Because of the assumption above that header lengths would be encoded
  428.      * in one byte, things need to be fixed, now that the actual lengths are known.
  429.      */
  430.     header_shift = 0;
  431.     out_length = packet_end - var_list_start;
  432.     if (out_length >= 0x80){
  433.     header_shift++;
  434.     if (out_length > 0xFF)
  435.         header_shift++;
  436.     }
  437.     if (header_shift){
  438.     shift_array(var_list_start, packet_end - var_list_start, header_shift);
  439.     packet_end += header_shift;
  440.     var_list_start += header_shift;
  441.     }
  442.  
  443.     /* Now rebuild header with the actual lengths */
  444.     dummyLen = packet_end - var_list_start;
  445.     if (asn_build_header(headerP, &dummyLen, (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), dummyLen) == NULL){
  446.     return SNMP_ERR_TOOBIG;    /* bogus error ???? */
  447.     }
  448.     *index = 0;
  449.     return SNMP_ERR_NOERROR;
  450. }
  451.  
  452. /*
  453.  * create a packet identical to the input packet, except for the error status
  454.  * and the error index which are set according to the input variables.
  455.  * Returns 1 upon success and 0 upon failure.
  456.  */
  457. int
  458. create_identical(snmp_in, snmp_out, snmp_length, errstat, errindex)
  459.     u_char        *snmp_in;
  460.     u_char        *snmp_out;
  461.     int            snmp_length;
  462.     long        errstat, errindex;
  463. {
  464.     register u_char *data;
  465.     u_char        type;
  466.     u_long        dummy;
  467.     int            length, headerLength;
  468.     register u_char *headerPtr, *reqidPtr, *errstatPtr, *errindexPtr, *varListPtr;
  469.  
  470.     bcopy((char *)snmp_in, (char *)snmp_out, snmp_length);
  471.     length = snmp_length;
  472.     headerPtr = snmp_auth_parse(snmp_out, &length, sid, &sidlen, (long *)&dummy);
  473.     if (headerPtr == NULL)
  474.     return 0;
  475.     reqidPtr = asn_parse_header(headerPtr, &length, (u_char *)&dummy);
  476.     if (reqidPtr == NULL)
  477.     return 0;
  478.     headerLength = length;
  479.     errstatPtr = asn_parse_int(reqidPtr, &length, &type, &dummy, sizeof dummy);    /* request id */
  480.     if (errstatPtr == NULL)
  481.     return 0;
  482.     errindexPtr = asn_parse_int(errstatPtr, &length, &type, &dummy, sizeof dummy);    /* error status */
  483.     if (errindexPtr == NULL)
  484.     return 0;
  485.     varListPtr = asn_parse_int(errindexPtr, &length, &type, &dummy, sizeof dummy);    /* error index */
  486.     if (varListPtr == NULL)
  487.     return 0;
  488.  
  489.     data = asn_build_header(headerPtr, &headerLength, GET_RSP_MSG, headerLength);
  490.     if (data != reqidPtr)
  491.     return 0;
  492.     length = snmp_length;
  493.     type = (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER);
  494.     data = asn_build_int(errstatPtr, &length, type, (u_char *)&errstat, sizeof errstat);
  495.     if (data != errindexPtr)
  496.     return 0;
  497.     data = asn_build_int(errindexPtr, &length, type, (u_char *)&errindex, sizeof errindex);
  498.     if (data != varListPtr)
  499.     return 0;
  500.     packet_end = snmp_out + snmp_length;
  501.     return 1;
  502. }
  503.  
  504. #ifdef KINETICS
  505. struct pbuf *
  506. definitelyGetBuf(){
  507.     register struct pbuf *p;
  508.  
  509.     K_PGET(PT_DATA, p);
  510.     while(p == 0){
  511. #ifdef notdef
  512.     if (pq->pq_head != NULL){
  513.         K_PDEQ(SPLIMP, pq, p);
  514.         if (p) K_PFREE(p);
  515.     } else if (sendq->pq_head != NULL){
  516.         K_PDEQ(SPLIMP, sendq, p);
  517.         if (p) K_PFREE(p);
  518.     }
  519. #endif
  520.     K_PGET(PT_DATA, p);
  521.     }
  522.     return p;
  523. }
  524. #endif
  525.  
  526. int
  527. snmp_access(acl, community, rw)
  528.     u_short     acl;
  529.     int        community;
  530.     int        rw;
  531. {
  532.     /*
  533.      * Each group has 2 bits, the more significant one is for read access,
  534.      * the less significant one is for write access.
  535.      */
  536.  
  537.     community <<= 1;    /* multiply by two two shift two bits at a time */
  538.     if (rw == READ){
  539.     return (acl & (2 << community));    /* return the correct bit */
  540.     } else {
  541.     return (acl & (1 << community));
  542.     }
  543. }
  544.  
  545. int
  546. get_community(sessionid)
  547.     u_char    *sessionid;
  548. {
  549.     int    count;
  550.  
  551.     for(count = 0; count < NUM_COMMUNITIES; count++){
  552.     if (!strcmp(communities[count], sessionid))
  553.         break;
  554.     }
  555.     if (count == NUM_COMMUNITIES)
  556.     return -1;
  557.     return count;
  558. }
  559.  
  560. int
  561. goodValue(inType, inLen, actualType, actualLen)
  562.     u_char    inType, actualType;
  563.     int        inLen, actualLen;
  564. {
  565.     int    type;
  566.  
  567.     if (inLen > actualLen)
  568.     return FALSE;
  569.     /* convert intype to internal representation */
  570.     type = inType == ASN_INTEGER ? INTEGER : inType == ASN_OCTET_STR ? STRING : inType == ASN_OBJECT_ID ? OBJID : NULLOBJ;
  571.     return (type == actualType);
  572. }
  573.  
  574. setVariable(var_val, var_val_type, var_val_len, statP, statLen)
  575.     u_char  *var_val;
  576.     u_char  var_val_type;
  577.     int        var_val_len;
  578.     u_char  *statP;
  579.     int        statLen;
  580. {
  581.     int        buffersize = 1000;
  582.  
  583.     switch(var_val_type){
  584.     case ASN_INTEGER:
  585.     case COUNTER:
  586.     case GAUGE:
  587.     case TIMETICKS:
  588.         asn_parse_int(var_val, &buffersize, &var_val_type, (u_long *)statP, statLen);
  589.         break;
  590.     case ASN_OCTET_STR:
  591.     case IPADDRESS:
  592.     case OPAQUE:
  593.         asn_parse_string(var_val, &buffersize, &var_val_type, statP, &statLen);
  594.         break;
  595.     case ASN_OBJECT_ID:
  596.         asn_parse_objid(var_val, &buffersize, &var_val_type, statP, &statLen);
  597.         break;
  598.     }
  599. }
  600.  
  601.  
  602. shift_array(begin, length, shift_amount)
  603.     u_char        *begin;
  604.     register int    length;
  605.     int            shift_amount;
  606. {
  607.     register u_char    *old, *new;
  608.  
  609.     if (shift_amount >= 0){
  610.     old = begin + length - 1;
  611.     new = old + shift_amount;
  612.  
  613.     while(length--)
  614.         *new-- = *old--;
  615.     } else {
  616.     old = begin;
  617.     new = begin + shift_amount;
  618.  
  619.     while(length--)
  620.         *new++ = *old++;
  621.     }
  622. }
  623.  
  624.